package com.director.core.json.impl.gson;

import com.director.core.ResultWithStrategy;
import com.director.core.ReturnDataStrategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

/**
 * @author Simone Ricciardi
 * @version 1.0, 10/25/2011
 */
public class ResultWithStrategyTypeAdapter implements JsonSerializer<ResultWithStrategy> {

   @Override
   public JsonElement serialize(ResultWithStrategy src, Type typeOfSrc, JsonSerializationContext context) {
      ReturnDataStrategy strategy = src.getStrategy();
      Map<Object, List<String>> serializationMap = strategy.buildSerializationMap(src.getResult());
      GsonBuilder gsonBuilder = new GsonBuilder();
      for(Object o : serializationMap.keySet()) {
         gsonBuilder.registerTypeAdapter(o.getClass(), new ObjectAdapter(serializationMap));
      }
      return gsonBuilder.create().toJsonTree(src.getResult());
   }

   class ObjectAdapter implements JsonSerializer<Object>{

      private Map<Object, List<String>> serializationMap;

      ObjectAdapter(Map<Object, List<String>> serializationMap) {
         this.serializationMap = serializationMap;
      }

      @Override
      public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
         try {

            if(!this.serializationMap.containsKey(src)) {
               return new Gson().toJsonTree(src);
            }

            JsonObject srcElement = new JsonObject();
            List<String> fieldNames = this.serializationMap.get(src);
            for(String fieldName : fieldNames) {
               Field field = src.getClass().getDeclaredField(fieldName);
               field.setAccessible(true);
               Object fieldValue = field.get(src);
               srcElement.add(fieldName, context.serialize(fieldValue));
            }
            return srcElement;
         } catch(Exception e) {
            throw new JsonParseException(e);
         }
      }
   }

}
